package 我的JDK基础数据结构.BlockingQueue;

import lombok.SneakyThrows;

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProductAndConsume {

    private static AtomicInteger a = new AtomicInteger();
    private static AtomicInteger b = new AtomicInteger();

    /**
     * 基于Lock手写版
     */
    private static int capacity = 10;
    private static AtomicInteger count = new AtomicInteger();
    /**
     * 想对Condition操作,必须是获取了它的lock锁后才可以操作
     */
    private static Lock takeLock = new ReentrantLock();
    private static Condition okTake = takeLock.newCondition();
    private static Lock putLock = new ReentrantLock();
    private static Condition okPut = putLock.newCondition();

    private static LinkedList<Integer> linkedList = new LinkedList<>();

    static class Product {

        @SneakyThrows
        public Product() {

            for (int i = 0; i < 1000; i++) {

                try {
                    putLock.lockInterruptibly();//可打断的获取锁
                    if (count.get() == capacity) {
                        // 容量满了
                        okPut.await();//等待放入
                    }
                    linkedList.addLast(i);
                    a.incrementAndGet();
                    int c = count.incrementAndGet();

                    if (c < capacity) {
                        // 当前容量不足,唤醒其他线程放入
                        okPut.signal();//为了其他线程可以放入
                    }
                    // 当前容量为1,说明放入前容量已经为0了,唤醒消费者
                    if (c == 1) {
                        /**
                         * 我锁中有你,你锁中有我
                         */
                        this.okTake();
                    }

                } finally {
                    putLock.unlock();
                }

            }

        }

        @SneakyThrows
        private void okTake() {
            try {
                takeLock.lockInterruptibly();
                okTake.signal();
            } finally {
                takeLock.unlock();
            }
        }

    }

    static class Consume {

        @SneakyThrows
        public Consume() {

            for (int i = 0; i < 1000; i++) {

                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                    takeLock.lockInterruptibly();
                    if (count.get() == 0) {
                        okTake.await();
                    }
                    System.out.println("产品:" + linkedList.removeFirst() + "线程:" + Thread.currentThread().getName());
                    b.incrementAndGet();
                    int c = count.decrementAndGet();
                    if (c > 0) {
                        okTake.signal();//为了其他线程可以消费
                    }
                    if (c == capacity - 1) {
                        this.okPut();
                    }
                } finally {
                    takeLock.unlock();
                }
            }

        }

        @SneakyThrows
        private void okPut() {
            try {
                putLock.lockInterruptibly();
                okPut.signal();
            } finally {
                putLock.unlock();
            }
        }

    }

    @SneakyThrows
    public static void main(String[] args) {
        new Thread(() -> {
            Product product = new Product();
        }).start();
        new Thread(() -> {
            Consume consume = new Consume();
        }).start();
        new Thread(() -> {
            Product product = new Product();
        }).start();
        new Thread(() -> {
            Consume consume = new Consume();
        }).start();
        new Thread(() -> {
            Product product = new Product();
        }).start();
        new Thread(() -> {
            Consume consume = new Consume();
        }).start();
        new Thread(() -> {
            Product product = new Product();
        }).start();
        Thread thread = new Thread(() -> {
            Consume consume = new Consume();
        });
        thread.start();

        thread.join();

        System.out.println(a);
        System.out.println(b);

    }

}
